﻿using System.Security.Cryptography;

namespace SharpZip.Net
{
    public class AesEncryptorKey
    {
        public string Key { get; }

        public string IV { get; }

        public byte[] KeyBytes { get; }

        public byte[] IVBytes { get; }

        public AesEncryptorKey(string key, string iv)
        {
            if (key == null || key.Length <= 0)
                throw new ArgumentNullException("Key");
            if (iv == null || iv.Length <= 0)
                throw new ArgumentNullException("IV");

            Key = key;
            IV = iv;
            KeyBytes = Convert.FromBase64String(Key);
            IVBytes = Convert.FromBase64String(IV);
        }
    }

    internal static class AesEncryptor
    {
        public static AesEncryptorKey CreateKey()
        {
            using var aes = Aes.Create();
            return new AesEncryptorKey(
                Convert.ToBase64String(aes.Key),
                Convert.ToBase64String(aes.IV));
        }

        public static byte[] Encrypt(byte[] plainText, AesEncryptorKey key)
        {
            // Check arguments.
            if (plainText == null || plainText.Length <= 0)
                throw new ArgumentNullException("plainText");

            byte[] encrypted;

            // Create an Aes object
            // with the specified key and IV.
            using (Aes aesAlg = Aes.Create())
            {
                aesAlg.Key = key.KeyBytes;
                aesAlg.IV = key.IVBytes;

                // Create an encryptor to perform the stream transform.
                ICryptoTransform encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV);

                // Create the streams used for encryption.
                using (MemoryStream msEncrypt = new MemoryStream())
                {
                    using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
                    {
                        csEncrypt.Write(plainText);
                    }

                    encrypted = msEncrypt.ToArray();
                }
            }

            // Return the encrypted bytes from the memory stream.
            return encrypted;
        }

        public static byte[] Decrypt(byte[] cipherText, AesEncryptorKey key)
        {
            // Check arguments.
            if (cipherText == null || cipherText.Length <= 0)
                throw new ArgumentNullException("cipherText");

            // Declare the string used to hold
            // the decrypted text.
            byte[] plaintext;

            // Create an Aes object
            // with the specified key and IV.
            using (Aes aesAlg = Aes.Create())
            {
                aesAlg.Key = key.KeyBytes;
                aesAlg.IV = key.IVBytes;

                // Create a decryptor to perform the stream transform.
                ICryptoTransform decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV);

                // Create the streams used for decryption.
                using (MemoryStream msDecrypt = new MemoryStream(cipherText))
                {
                    using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
                    {
                        // Read the decrypted bytes from the decrypting stream
                        // and place them in a string.
                        plaintext = ReadAllBytes(csDecrypt);
                    }
                }
            }

            return plaintext;
        }

        private static byte[] ReadAllBytes(Stream stream)
        {
            using MemoryStream ms = new MemoryStream();
            stream.CopyTo(ms);
            return ms.ToArray();
        }
    }
}